package com.framework.core.aes;

import org.apache.log4j.Logger;
import org.bouncycastle.util.encoders.Base64;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.AlgorithmParameters;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.Security;

/**
* @Description:   AES加密解密 （算法：AES/ECB/PKCS5Padding）
* @Author:         wangzhiqiang
* @CreateDate:     2019-04-09 15:51
* @Version:        1.0
*/
public class AESCoder {

    private static final Logger log = Logger.getLogger(AESCoder.class);

    /**
     * 密钥算法
     */
    private static final String KEY_ALGORITHM = "AES";

    private static final String DEFAULT_CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";

    /**
     * 初始化密钥
     *
     * @return byte[] 密钥
     * @throws Exception
     */
    public static byte[] initSecretKey() {
        //返回生成指定算法的秘密密钥的 KeyGenerator 对象
        KeyGenerator kg = null;
        try {
            kg = KeyGenerator.getInstance(KEY_ALGORITHM);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            return new byte[0];
        }
        //初始化此密钥生成器，使其具有确定的密钥大小
        //AES 要求密钥长度为 128
        kg.init(128);
        //生成一个密钥
        SecretKey  secretKey = kg.generateKey();
        return secretKey.getEncoded();
    }

    /**
     * 转换密钥
     *
     * @param key	二进制密钥
     * @return 密钥
     */
    private static Key toKey(byte[] key){
        //生成密钥
        return new SecretKeySpec(key, KEY_ALGORITHM);
    }

    /**
     * 加密
     *
     * @param data	待加密数据（字符串）
     * @param key	密钥（字符串）
     * @return byte[]	加密数据
     * @throws Exception
     */
    public static byte[] encrypt(String data, String key) throws Exception{
        byte[] b_data = Base64.decode(data);
        byte[] b_key = Base64.decode(key);
        return encrypt(b_data, b_key,DEFAULT_CIPHER_ALGORITHM);
    }

    /**
     * 加密
     *
     * @param data	待加密数据（字符串）
     * @param key	密钥（字符串）
     * @param AlgorithmParameters 加密初始向量
     * @return byte[]	加密数据
     * @throws Exception
     */
    public static byte[] encrypt(String data, String key, String AlgorithmParameters) throws Exception{
        log.info("b_data:"+data);
        byte[] b_data = Base64.decode(data);
        byte[] b_key = Base64.decode(key);
        byte[] ivData = Base64.decode(AlgorithmParameters);
        Key _key = toKey(b_key);
        AlgorithmParameters al = generateIv(ivData);
        return encrypt(b_data, _key,DEFAULT_CIPHER_ALGORITHM, al);
    }

    /**
     * 加密
     *
     * @param data	待加密数据
     * @param key	密钥
     * @return byte[]	加密数据
     * @throws Exception
     */
    public static byte[] encrypt(byte[] data,Key key) throws Exception{
        return encrypt(data, key,DEFAULT_CIPHER_ALGORITHM);
    }

    /**
     * 加密
     *
     * @param data	待加密数据
     * @param key	二进制密钥
     * @return byte[]	加密数据
     * @throws Exception
     */
    public static byte[] encrypt(byte[] data,byte[] key) throws Exception{
        return encrypt(data, key,DEFAULT_CIPHER_ALGORITHM);
    }

    /**
     * 加密
     *
     * @param data	待加密数据
     * @param key	二进制密钥
     * @param cipherAlgorithm	加密算法/工作模式/填充方式
     * @return byte[]	加密数据
     * @throws Exception
     */
    public static byte[] encrypt(byte[] data,byte[] key,String cipherAlgorithm) throws Exception{
        //还原密钥
        Key k = toKey(key);
        return encrypt(data, k, cipherAlgorithm);
    }

    /**
     * 加密
     *
     * @param data	待加密数据
     * @param key	密钥
     * @param cipherAlgorithm	加密算法/工作模式/填充方式
     * @return byte[]	加密数据
     * @throws Exception
     */
    public static byte[] encrypt(byte[] data,Key key,String cipherAlgorithm) throws Exception{
        //实例化
        Cipher cipher = Cipher.getInstance(cipherAlgorithm);
        //使用密钥初始化，设置为加密模式
        cipher.init(Cipher.ENCRYPT_MODE, key);
        //执行操作
        return cipher.doFinal(data);
    }

    /**
     * 加密
     *
     * @param data	待加密数据
     * @param key	密钥
     * @param cipherAlgorithm	加密算法/工作模式/填充方式
     * @param algorithmParameters 初始向量
     * @return byte[]	加密数据
     * @throws Exception
     */
    public static byte[] encrypt(byte[] data,Key key,String cipherAlgorithm, AlgorithmParameters algorithmParameters) throws Exception{
        Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
        //实例化
        Cipher cipher = Cipher.getInstance(cipherAlgorithm);
        //使用密钥初始化，设置为加密模式，设置初始向量
        cipher.init(Cipher.ENCRYPT_MODE, key, algorithmParameters);
        //执行操作
        return cipher.doFinal(data);
    }


    /**
     * 解密
     *
     * @param data	待解密数据
     * @param key	二进制密钥
     * @return byte[]	解密数据
     * @throws Exception
     */
    public static byte[] decrypt(String data, String key) throws Exception{
        byte[] b_data = Base64.decode(data);
        byte[] b_key = Base64.decode(key);
        return decrypt(b_data, b_key,DEFAULT_CIPHER_ALGORITHM);
    }

    /**
     * 解密
     *
     * @param data	待解密数据
     * @param key	二进制密钥
     * @return byte[]	解密数据
     * @throws Exception
     */
    public static byte[] decrypt(String data, String key, String algorithmParameters) throws Exception{
        byte[] b_data = Base64.decode(data);
        byte[] b_key = Base64.decode(key);
        Key _key = toKey(b_key);
        return decrypt(b_data, _key,DEFAULT_CIPHER_ALGORITHM, generateIv(algorithmParameters));
    }

    /**
     * 解密
     *
     * @param data	待解密数据
     * @param key	二进制密钥
     * @return byte[]	解密数据
     * @throws Exception
     */
    public static byte[] decrypt(byte[] data,byte[] key) throws Exception{
        return decrypt(data, key,DEFAULT_CIPHER_ALGORITHM);
    }

    /**
     * 解密
     *
     * @param data	待解密数据
     * @param key	密钥
     * @return byte[]	解密数据
     * @throws Exception
     */
    public static byte[] decrypt(byte[] data,Key key) throws Exception{
        return decrypt(data, key,DEFAULT_CIPHER_ALGORITHM);
    }

    /**
     * 解密
     *
     * @param data	待解密数据
     * @param key	二进制密钥
     * @param cipherAlgorithm	加密算法/工作模式/填充方式
     * @return byte[]	解密数据
     * @throws Exception
     */
    public static byte[] decrypt(byte[] data,byte[] key,String cipherAlgorithm) throws Exception{
        //还原密钥
        Key k = toKey(key);
        return decrypt(data, k, cipherAlgorithm);
    }

    /**
     * 解密
     *
     * @param data	待解密数据
     * @param key	密钥
     * @param cipherAlgorithm	加密算法/工作模式/填充方式
     * @return byte[]	解密数据
     * @throws Exception
     */
    public static byte[] decrypt(byte[] data,Key key,String cipherAlgorithm) throws Exception{
        //实例化
        Cipher cipher = Cipher.getInstance(cipherAlgorithm);
        //使用密钥初始化，设置为解密模式
        cipher.init(Cipher.DECRYPT_MODE, key);
        //执行操作
        return cipher.doFinal(data);
    }

    /**
     * 解密
     *
     * @param data	待解密数据
     * @param key	密钥
     * @param cipherAlgorithm	加密算法/工作模式/填充方式
     * @param algorithmParameters 初始向量
     * @return byte[]	解密数据
     * @throws Exception
     */
    public static byte[] decrypt(byte[] data,Key key,String cipherAlgorithm, AlgorithmParameters algorithmParameters) throws Exception{
        Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
        //实例化
        Cipher cipher = Cipher.getInstance(cipherAlgorithm);
        //使用密钥初始化，设置为解密模式
        cipher.init(Cipher.DECRYPT_MODE, key, algorithmParameters);
        //执行操作
        return cipher.doFinal(data);
    }

    /**
     * 加密、解密初始向量
     * @param iv
     * @return
     * @throws Exception
     */
    public static AlgorithmParameters generateIv(String iv) throws Exception{
        AlgorithmParameters params = AlgorithmParameters.getInstance("AES");
        params.init(new IvParameterSpec(Base64.decode(iv)));
        return params;
    }

    /**
     * 加密、解密初始向量
     * @param iv
     * @return
     * @throws Exception
     */
    public static AlgorithmParameters generateIv(byte[] iv) throws Exception{
        AlgorithmParameters params = AlgorithmParameters.getInstance("AES");
        params.init(new IvParameterSpec(iv));
        return params;
    }

    private static String  showByteArray(byte[] data){
        if(null == data){
            return null;
        }
        StringBuilder sb = new StringBuilder("{");
        for(byte b:data){
            sb.append(b).append(",");
        }
        sb.deleteCharAt(sb.length()-1);
        sb.append("}");
        return sb.toString();
    }

    public static void main(String[] args) throws Exception {
//        byte[] key = initSecretKey();
//        System.out.println("key："+showByteArray(key));
//
//        Key k = toKey(key);
//
//        String data ="AES数据";
//        System.out.println("加密前数据: string:"+data);
//        System.out.println("加密前数据: byte[]:"+showByteArray(data.getBytes()));
//        System.out.println();
//        byte[] encryptData = encrypt(data.getBytes(), k);
//        System.out.println("加密后数据: byte[]:"+showByteArray(encryptData));
//        System.out.println("加密后数据: hexStr:"+encryptData);
//        System.out.println();
//        byte[] decryptData = decrypt(encryptData, k);
//        System.out.println("解密后数据: byte[]:"+showByteArray(decryptData));
//        System.out.println("解密后数据: string:"+new String(decryptData));


        String key = "0k5/9A7M3SbM4zvNC1Dxlg==";
        String iv = "jSMLIzkKQTsQ9iWW7vRSDA==";
        String data ="AES数据";

        System.out.println("key："+key);
        System.out.println("加密初始向量："+iv);
        System.out.println("加密前数据: string:"+data);
        System.out.println();
//        byte[] encryptData = encrypt(data, key, iv);
//        System.out.println("加密后数据: byte[]:"+showByteArray(encryptData));
//        System.out.println("加密后数据: hexStr:"+encryptData);
//        System.out.println();
//        byte[] decryptData = decrypt(showByteArray(encryptData), key, iv);

        String encryptedData = "YXSDdZblhmiPYoXUYdftdVyErVnFGJGwxrv3dBOaR8UCuQAo2hexfzMteVFek6rwAQDwWU6oUjgrPIT0vYBQl0HfeuSC4KboAva/xh7YJyxp2GwNzjxFkOUBcfMktl4/bk5W6i0uczDZqCCrn0uHKIe/NZ7qO4tImjD2P8oAAEclrW1ohJQzn3JUOyH45XnocRQkB2Q/V4VCqcttl9wO/nKNhCSo3lzQ3TUmpl2/abUgstqJOzgLKacILKpuInF7OAtjDPGJ1cUA+1uSNDUjfxcPm4a91K3tG6WezqgwguHsGo4AJa2WFxkTVZXidiFZULpVbfnXQ9mnU4cQUDnaXrW42ezvddhT2GRaWj3AgzqjDXhl3uamPVVLNlTN3JkdyLpGqWFlhn1TpH/cexyegd+pgzwNygF+E34aKUcXJfvsVfTuGno/GBVII/la8NKi/LpQBI4g6yOyu8gSLus9RNFezeb3Cw4Z71L6yg/rhnU=";
        String sessionKey = "0k5/9A7M3SbM4zvNC1Dxlg==";

        byte[] decryptData2 = decrypt(encryptedData, sessionKey, iv);
        System.out.println("解密后数据: byte[]:"+showByteArray(decryptData2));
        System.out.println("解密后数据: string:"+new String(decryptData2));

    }
}
